// setup.cpp
// Customizer_Base
// Protea Customizer/Customizer Pro / Generic setup and other misc. functions
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// Please send correspondence to:
//
// theproteaproject@gmail.com
//

#include "base_setup.h"

#include <stdarg.h>
#include <time.h>

// Include specific field defns
#include "../../../Fields/field_defns.h"


////////////////////////////////////
//
// Transactions definitions
//
////////////////////////////////////

// DESIGNER EXTENSIONS
TRANSACTION( DesignerGetFormatInfo );
TRANSACTION( DesignerGetMessages );
TRANSACTION( DesignerGetLanguages );
TRANSACTION( DesignerGetDefaults );

// Pulled from other sources
TRANSACTION( RevFormat );
TRANSACTION( RevFormats );
TRANSACTION( RevFormatsComponent );
TRANSACTION( PrintFormat );

TRANSACTION( RevLitGroup );

TRANSACTION( RevDataStructure );

TRANSACTION( ReviewTransaction );
TRANSACTION( RevTransactions );

////////////////////////////////////
//
// Global definitions
//
////////////////////////////////////
const char *invalid = 

	// ANSI SQL Standard Keywords (SQL 92)
	",ABSOLUTE,ACTION,ADA,ADD,ALL,ALLOCATE,ALTER,AND,ANY"	
	",ARE,AS,ASC,ASSERTION,AT,AUTHORIZATION,AVG,BEGIN"
	",BETWEEN,BIT,BIT_LENGTH,BOTH,BY,C,CASCADE,CASCADED"
	",CASE,CAST,CATALOG,CATALOG_NAME,CHAR,CHAR_LENGTH,CHARACTER"
	",CHARACTER_LENGTH,CHARACTER_SET_CATALOG,CHARACTER_SET_NAME"
	",CHARACTER_SET_SCHEMA,CHECK,CLASS_ORIGIN,CLOSE,COALESCE"
	",COBOL,COLLATE,COLLATION,COLLATION_CATALOG,COLLATION_NAME"
	",COLLATION_SCHEMA,COLUMN,COLUMN_NAME,COMMAND_FUNCTION,COMMIT"
	",COMMITTED,CONDITION_NUMBER,CONNECT,CONNECTION,CONNECTION_NAME"
	",CONSTRAINT,CONSTRAINT_CATALOG,CONSTRAINT_NAME,CONSTRAINT_SCHEMA"
	",CONSTRAINTS,CONTINUE,CONVERT,CORRESPONDING,COUNT,CREATE"
	",CROSS,CURRENT,CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP"
	",CURRENT_USER,CURSOR,CURSOR_NAME,DATA,DATE"
	",DATETIME_INTERVAL_PRECISION,DATETIME_INTERVAL_CODE,DAY"
	",DEALLOCATE,DEC,DECIMAL,DECLARE,DEFAULT,DEFERRABLE"
	",DEFERRED,DELETE,DESC,DESCRIBE,DESCRIPTOR,DIAGNOSTICS"
	",DISCONNECT,DISTINCT,DOMAIN,DOUBLE,DROP,DYNAMIC_FUNCTION"
	",ELSE,END,END-EXEC,ESCAPE,EXCEPT,EXCEPTION,EXEC,EXECUTE"
	",EXISTS,EXTERNAL,EXTRACT,FALSE,FETCH,FIRST,FLOAT,FOR"
	",FOREIGN,FORTRAN,FOUND,FROM,FULL,GET,GLOBAL,GO,GOTO"
	",GRANT,GROUP,HAVING,HOUR,IDENTITY,IMMEDIATE,IN,INDICATOR"
	",INITIALLY,INNER,INPUT,INSENSITIVE,INSERT,INT,INTEGER"
	",INTERSECT,INTERVAL,INTO,IS,ISOLATION,JOIN,KEY,LANGUAGE"
	",LAST,LEADING,LEFT,LENGTH,LEVEL,LIKE,LOCAL,LOWER,MATCH"
	",MAX,MESSAGE_LENGTH,MESSAGE_OCTET_LENGTH,MESSAGE_TEXT,MIN"
	",MINUTE,MODULE,MONTH,MORE,MUMPS,NAME,NAMES,NATIONAL"
	",NATURAL,NCHAR,NEXT,NO,NOT,NULL,NULLABLE,NULLIF,NUMBER"
	",NUMERIC,OCTET_LENGTH,OF,ON,ONLY,OPEN,OPTION,OR,ORDER"
	",OUTER,OUTPUT,OVERLAPS,PAD,PARTIAL,PASCAL,PLI,POSITION"
	",PRECISION,PREPARE,PRESERVE,PRIMARY,PRIOR,PRIVILEGES"
	",PROCEDURE,PUBLIC,READ,REAL,REFERENCES,RELATIVE,REPEATABLE"
	",RESTRICT,RETURNED_LENGTH,RETURNED_OCTET_LENGTH"
	",RETURNED_SQLSTATE,REVOKE,RIGHT,ROLLBACK,ROW_COUNT,ROWS"
	",SCALE,SCHEMA,SCHEMA_NAME,SCROLL,SECOND,SECTION,SELECT"
	",SERIALIZABLE,SERVER_NAME,SESSION,SESSION_USER,SET,SIZE"
	",SMALLINT,SOME,SPACE,SQL,SQLCODE,SQLERROR,SQLSTATE"
	",SUBCLASS_ORIGIN,SUBSTRING,SUM,SYSTEM_USER,TABLE,TABLE_NAME"
	",TEMPORARY,THEN,TIME,TIMESTAMP,TIMEZONE_HOUR,TIMEZONE_MINUTE"
	",TO,TRAILING,TRANSACTION,TRANSLATE,TRANSLATION,TRIM,TRUE"
	",TYPE,UNCOMMITTED,UNION,UNIQUE,UNKNOWN,UNNAMED,UPDATE"
	",UPPER,USAGE,USER,USING,VALUE,VALUES,VARCHAR,VARYING"
	",VIEW,WHEN,WHENEVER,WHERE,WITH,WORK,WRITE,YEAR,ZONE"

	// ANSI SQL Standard On Alert Keywords (SQL 92)
	",AFTER,ALIAS,ASYNC,BEFORE,BOOLEAN,BREADTH,COMPLETION"
	",CALL,CYCLE,DEPTH,DICTIONARY,EACH,ELSEIF,EQUALS,GENERAL"
	",IF,IGNORE,LEAVE,LESS,LIMIT,LOOP,MODIFY,NEW,NONE"
	",OBJECT,OFF,OID,OLD,OPERATION,OPERATORS,OTHERS"
	",PARAMETERS,PENDANT,PREORDER,PRIVATE,PROTECTED,RECURSIVE"
	",REF,REFERENCING,REPLACE,RESIGNAL,RETURN,RETURNS,ROLE"
	",ROUTINE,ROW,SAVEPOINT,SEARCH,SENSITIVE,SEQUENCE,SIGNAL"
	",SIMILAR,SQLEXCEPTION,SQLWARNING,STRUCTURE,TEST,THERE"
	",TRIGGER,UNDER,VARIABLE,VIRTUAL,VISIBLE,WAIT,WHILE"
	",WITHOUT"

	// Oracle PL/SQL Keywords
	",ARRAY,AUTHID,BINARY_INTEGER,BODY,BULK,CHAR_BASE,CLUSTER"
	",COLLECT,COMMENT,COMPRESS,CONSTANT,CURRVAL,DO,ELSIF"
	",EXCLUSIVE,EXIT,EXTENDS,FORALL,FUNCTION,HEAP,INCREMENT,INDEX"
	",INTERFACE,JAVA,LIMITED,LOCK,LONG,MINUS,MLSLABEL,MOD"
	",MODE,NATURALN,NEXTVAL,NOCOPY,NOWAIT,NUMBER_BASE,OCIROWID"
	",OPAQUE,OPERATOR,ORGANIZATION,OUT,PACKAGE"
	",PARTITION,PCTFREE,PLS_INTEGER,POSITIVE,POSITIVEN,PRAGMA"
	",RAISE,RANGE,RAW,RECORD,RELEASE,REVERSE,ROWID,ROWNUM"
	",ROWTYPE,SEPARATE,SHARE,SQLERRM,START,STDDEV,SUBTYPE"
	",SUCCESSFUL,SYNONYM,SYSDATE,UID,USE,VALIDATE,VARCHAR2"
	",VARIANCE"

	// DB2 IBM SQL Keywords
	",SYSCAT,SYSFUN,SYSIBM,SYSSTAT,ACQUIRE,ALLOW,ASUTIME"
	",AUDIT,AUX,AUXILIARY,BINARY,BUFFERPOOL,CALLED,CAPTURE"
	",CCSID,COLLECTION,COLLID,CONCAT,CONDITION,CONTAINS"
	",COUNT_BIG,CURRENT_LC_PATH,CURRENT_PATH,CURRENT_SERVER"
	",CURRENT_TIMEZONE,DATABASE,DAYS,DBA,DBINFO,DBSPACE"
	",DB2GENERAL,DB2SQL,DETERMINISTIC,DISALLOW,DSSIZE,DYNAMIC"
	",EDITPROC,ERASE,FENCED,FIELDPROC,FILE,FINAL,FREE"
	",GENERATED,GRAPHIC,HANDLER,HOURS,IDENTIFIED,INOUT"
	",INTEGRITY,ISOBID,LABEL,LC_CTYPE,LINKTYPE,LOCALE,LOCATOR"
	",LOCATORS,LOCKSIZE,MICROSECOND,MICROSECONDS,MINUTES"
	",MODIFIES,MONTHS,NAMED,NHEADER,NODENAME,NODENUMBER,NULLS"
	",NUMPARTS,OBID,OPTIMIZATION,OPTIMIZE,PAGE,PAGES,PARAMETER"
	",PART,PATH,PCTINDEX,PIECESIZE,PLAN,PRIQTY,PROGRAM,PSID"
	",QUERYNO,READS,RECOVERY,RENAME,REPEAT,RESET,RESOURCE"
	",RESULT,RRN,RUN,SCHEDULE,SCRATCHPAD,SECONDS,SECQTY"
	",SECURITY,SIMPLE,SOURCE,SPECIFIC,STANDARD,STATIC"
	",STATISTICS,STAY,STOGROUP,STORES,STORPOOL,STYLE,SUBPAGES"
	",TABLESPACE,UNDO,UNTIL,VALIDPROC,VARIANT,VCAT,VOLUMES"
	",WLM,YEARS"

	// SQL Server Transact-SQL Keywords
	",BACKUP,BREAK,BROWSE,CHECKPOINT,CLUSTERED,COMPUTE,CONFIRM"
	",CONTAINSTABLE,CONTROLROW,DBCC,DENY,DISK,DISTRIBUTED"
	",DUMMY,DUMP,ERRLVL,ERROREXIT,FILLFACTOR,FLOPPY,FREETEXT"
	",FREETEXTTABLE,HOLDLOCK,IDENTITY_INSERT,IDENTITYCOL,KILL"
	",LINENO,LOAD,MIRROREXIT,NOCHECK,NONCLUSTERED,OFFSETS,ONCE"
	",OPENDATASOURCE,OPENQUERY,OPENROWSET,OVER,PERCENT,PERM"
	",PERMANENT,PIPE,PRINT,PROC,PROCESSEXIT,RAISERROR,READTEXT"
	",RECONFIGURE,REPLICATION,RESTORE,ROWCOUNT,ROWGUIDCOL,RULE"
	",SAVE,SETUSER,SHUTDOWN,TAPE,TEMP,TEXTSIZE,TOP,TRAN"
	",TRUNCATE,TSEQUAL,UPDATETEXT,WAITFOR,WRITETEXT"

	// Access Keywords
	",TEXT"

	// InterBase 6 SQL Keywords
	",ACTIVE,ADMIN,ASCENDING,AUTO,AUTODDL,BASED,BASENAME"
	",BASE_NAME,BLOB,BLOBEDIT,BUFFER,CACHE,CHECK_POINT_LEN"
	",CHECK_POINT_LENGTH,COMPILETIME,COMPUTED,CONDITIONAL"
	",CONTAINING,CSTRING,DB_KEY,DEBUG,DESCENDING,DISPLAY,ECHO"
	",EDIT,ENTRY_POINT,EVENT,EXTERN,FILTER,FREE_IT,GDSCODE"
	",GENERATOR,GEN_ID,GROUP_COMMIT_WAIT,GROUP_COMMIT_WAIT_TIME"
	",HELP,INACTIVE,INIT,INPUT_TYPE,ISQL,LC_MESSAGES,LC_TYPE"
	",LEV,LOGFILE,LOG_BUFFER_SIZE,LOG_BUF_SIZE,MANUAL,MAXIMUM"
	",MAXIMUM_SEGMENT,MAX_SEGMENT,MERGE,MESSAGE,MINIMUM"
	",MODULE_NAME,NOAUTO,NUM_LOG_BUFS,NUM_LOG_BUFFERS,OUTPUT_TYPE"
	",OVERFLOW,PAGELENGTH,PAGE_SIZE,PASSWORD,POST_EVENT,QUIT"
	",RAW_PARTITIONS,RDGB$DB_KEY,RECORD_VERSION,RESERV,RESERVING"
	",RETAIN,RETURNING_VALUES,RUNTIME,SEGMENT,SHADOW,SHARED"
	",SHELL,SHOW,SINGULAR,SNAPSHOT,SORT,STABILITY,STARTING"
	",STARTS,STATEMENT,SUB_TYPE,SUSPEND,TERMINATOR,VERSION"
	",WEEKDAY,YEARDAY"
	
	// MySQL Keywords
	",ANALYZE,ASENSITIVE,BIGINT,CHANGE,DATABASES,DAY_HOUR"
	",DAY_MICROSECOND,DAY_MINUTE,DAY_SECOND,DELAYED,DISTINCTROW"
	",DIV,DUAL,ENCLOSED,ESCAPED,EXPLAIN,FORCE,FULLTEXT"
	",HIGH_PRIORITY,HOUR_MICROSECOND,HOUR_MINUTE,HOUR_SECOND"
	",INFILE,ITERATE,KEYS,LINES,LOCALTIME,LOCALTIMESTAMP"
	",LONGBLOB,LONGTEXT,LOW_PRIORITY,MEDIUMBLOB,MEDIUMINT"
	",MEDIUMTEXT,MIDDLEINT,MINUTE_MICROSECOND,MINUTE_SECOND"
	",NO_WRITE_TO_BINLOG,OPTIONALLY,OUTFILE,PURGE,REGEXP,REQUIRE"
	",RLIKE,SCHEMAS,SECOND_MICROSECOND,SEPARATOR,SONAME,SPATIAL"
	",SQL_BIG_RESULT,SQL_CALC_FOUND_ROWS,SQL_SMALL_RESULT,SSL"
	",STRAIGHT_JOIN,TERMINATED,TINYBLOB,TINYINT,TINYTEXT,UNLOCK"
	",UNSIGNED,UTC_DATE,UTC_TIME,UTC_TIMESTAMP,VARBINARY"
	",VARCHARACTER,XOR,YEAR_MONTH,ZEROFILL"

	// Sybase Keywords
	",BOTTOM,CAPABILITY,CHAR_CONVERT,CONFLICT,CUBE,DELETING"
	",ENCRYPTED,ENDIF,EXISTING,EXTERNLOGIN,FORWARD,INDEX_LPAREN"
	",INSERTING,INSTALL,INSTEAD,INTEGRATED,IQ,LATERAL,LOGIN"
	",MEMBERSHIP,NOHOLDLOCK,NOTIFY,OPTIONS,PASSTHROUGH,PUBLICATION"
	",REFERENCE,REMOTE,REMOVE,REORGANIZE,ROLLUP,STOP,SUBTRANS"
	",SUBTRANSACTION,SYNCHRONIZE,SYNTAX_ERROR,UNBOUNDED"
	",UNIQUEIDENTIFIER,UPDATING,WINDOW,WITH_CUBE,WITH_LPAREN"
	",WITH_ROLLUP,WITHIN,";


void ValidateAppNumber( XCTransactionVars* vars ) {
	XCBuffer* Appl_Defn = NULL;

	// First check that Appl_number is not blank
	XCField* temp = vars->buffer->GetField( FLD_APPL_NBR );
	if (temp->IsBlank())
		vars->Errors->Add( ERR_IS_BLANK, SEV_CRITICAL, FLD_APPL_NBR, 1, temp );
	else {
		// Ensure that it is a valid application number
		Appl_Defn = vars->myapp->CreateBuffer( FMT_APPL_DEFN, false );
		try {
			Appl_Defn->MoveField( vars->buffer, FLD_APPL_NBR );
			if (!ApplDefnRetrieve( Appl_Defn, vars ))
				vars->Errors->Add( ERR_BAD_APP_NUMBER, SEV_CRITICAL, FLD_APPL_NBR, 1, temp );
		} catch( ... ) {
			vars->myapp->CleanupObjects( 1, &Appl_Defn );
			throw;
		};
		vars->myapp->CleanupObjects( 1, &Appl_Defn );
	};

	// If we make it here, then the application is OK
};




//@ SEC 5
//@ CHA ValidateName
//@ COD ValidateName
//@ GEN ValidateName return true as long as the supplied string can not be found in the
//@ GEN list of invalid SQL keywords.  Otherwise, it returns false.
void ValidateName( const int field_nbr, const XCBuffer* buffer, const long message, XCTransactionVars* vars ) {
	TRANS_INFUNC( ValidateName )

	XCField* field = buffer->GetField( field_nbr );
	const char* src = field->GetStringPtr();

	//@ Check for blank field name
	//@ ERR C (ERR_INV_OBJECT_NAME)
	if (field->IsBlank())
		vars->Errors->Add( ERR_INV_OBJECT_NAME, SEV_CRITICAL, field_nbr, 2, message, ERR_BLANK_NAME );

	//@ Check for valid first character
	int len = strlen( src );
	char upper = toupper( *src );
	if (upper < 'A' || upper > 'Z')
		vars->Errors->Add( ERR_INV_OBJECT_NAME, SEV_CRITICAL, field_nbr, 2, message, ERR_INV_FIRST_CHARACTER );

	//@ Check for other invalid characters
	for (int i=1; i<len; i++) {
		upper = toupper( src[i] );
		if (!((upper >= '0' && upper <= '9') || (upper >= 'A' && upper <= 'Z') || upper == '_')) {
			if (upper == ' ')
				vars->Errors->Add( ERR_INV_OBJECT_NAME, SEV_CRITICAL, field_nbr, 2, message, ERR_NO_SPACES_ALLOWED );
			else
				vars->Errors->Add( ERR_INV_OBJECT_NAME, SEV_CRITICAL, field_nbr, 2, message, ERR_INV_NAME_CHARACTER );
		};
	};

	//@ Check for database reserved words
	char* srch = new char[ strlen( src ) + 3 ];
	*srch = ',';
	XCStrUpperCase( src, srch+1 );
	strcat( srch, "," );
	if ( strstr( invalid, srch ) ) {
		delete [] srch;
		vars->Errors->Add( ERR_INV_OBJECT_NAME, SEV_CRITICAL, field_nbr, 2, message, ERR_DATABASE_RESERVED_WORD );
	};
	delete [] srch;

	TRANS_OUTFUNC();
};





//@ SEC 5
//@ CHA Lookup Number From Name
//@ COD LookupNumberFromName
//@ CRI "Name" field
//@ CRI APPL_NBR
//@ TAB "Buffer" Read I
//@ GEN LookupNumberFromName will take the name of an object and search through all
//@ GEN records of the object table looking for an object with that name.  When the 
//@ GEN object is found, the object is moved into the destination buffer.
bool LookupNumberFromName( XCTransactionVars* vars, XCBuffer* source, XCBuffer* dest, long number_field, long name_field, long buffer, long db_number_field, long db_name_field ) {
	if (db_name_field == 0)
		db_name_field = name_field;

	if (db_number_field == 0)
		db_number_field = number_field;

	long fmt_list[4] = { db_name_field, db_number_field, FLD_APPL_NBR, 0 };
	bool result = true;

	TRANS_INFUNC( LookupNumberFromName )

	XCSQLGen* query = vars->query;

	XCDBStatement* stmt = NULL;
	XCBuffer* temp_buf = NULL;

	try {
		if (source->GetField( name_field )) {
			// Create temp buffer
			temp_buf = App->CreateDynamicBuffer( fmt_list, false, buffer );
			XCField* fmtname = temp_buf->GetField( db_name_field );
			assert( fmtname );

			fmtname->SetFlags(3);
			temp_buf->MoveField( source, FLD_APPL_NBR );
			temp_buf->MoveUnlikeField( db_name_field, source, name_field );
			fmtname->Validate();

			// Create a select statement to locate the number
			query->MakeSELECT( temp_buf );
			if ( number_field == FLD_APPL_NBR && buffer == FMT_APPL_DEFN ) {
				// We are comparing against the APPL_DEFN table, don't add extra where clause
				query->AddWHERE( 1,
					temp_buf->GetField( db_name_field ) );
			} else {
				query->AddWHERE( 2,
					temp_buf->GetField( FLD_APPL_NBR ),
					temp_buf->GetField( db_name_field ) );
			};

			// Execute the query
			stmt = vars->dbtxn->ExecuteSQL( query );
			if (stmt->Eof()) {
				result = false;
				dest->GetField( number_field )->Clear();
			} else
				dest->MoveUnlikeField( number_field, temp_buf, db_number_field );
		} else if (!source->GetField( number_field ))
			result = false;
	} catch( ... ) {
		vars->myapp->CleanupObjects( 2, &stmt, &temp_buf );
		throw;
	};
	vars->myapp->CleanupObjects( 2, &stmt, &temp_buf );

	TRANS_OUTFUNCRET( result )
};





//@ SEC 5
//@ CHA Lookup Name From Number
//@ COD LookupNameFromNumber
//@ CRI "Name" field
//@ CRI APPL_NBR
//@ TAB "Buffer" Read I
//@ GEN LookupNameFromNumber will take an object name and search through all of the object table
//@ GEN records looking for an object with that number.  When the object is found, it moves
//@ GEN the object name into the destination buffer.
bool LookupNameFromNumber( XCTransactionVars* vars, XCBuffer* source, XCBuffer* dest, long number_field, long name_field, long buffer, long db_number_field, long db_name_field ) {
	bool result = true;

	if (db_name_field == 0)
		db_name_field = name_field;

	if (db_number_field == 0)
		db_number_field = number_field;

	long fmt_list[4] = { db_name_field, db_number_field, FLD_APPL_NBR, 0 };

	TRANS_INFUNC( LookupNameFromNumber )

	XCSQLGen* query = vars->query;
	XCDBStatement* stmt = NULL;
	XCBuffer* temp_buf = NULL;

	try {
		if (source->GetField( number_field )) {
			// Create temp buffer
			temp_buf = App->CreateDynamicBuffer( fmt_list, false, buffer );
			temp_buf->MoveField( source, FLD_APPL_NBR );
			temp_buf->MoveUnlikeField( db_number_field, source, number_field );

			// Create a select statement to locate the number
			query->MakeSELECT( temp_buf );
			query->AddWHERE( 2, 
				temp_buf->GetField( db_number_field ),
				temp_buf->GetField( FLD_APPL_NBR ));

			// Submit the sql statement
			stmt = vars->dbtxn->ExecuteSQL( query );
			if (stmt->Eof()) {
				vars->Errors->Add( ERR_INVALID, SEV_ERROR, number_field, 1, temp_buf->GetField( db_number_field )->GetFieldName() );
				dest->Clear();
				result = false;
			} else
				dest->MoveUnlikeField( name_field, temp_buf, db_name_field );
		} else if (!source->GetField( name_field )) {
			vars->Errors->Add( ERR_INVALID, SEV_ERROR, 0, 1, "name field" );
			result = false;
		};
	} catch( ... ) {
		vars->myapp->CleanupObjects( 2, &stmt, &temp_buf );
		throw;
	};
	vars->myapp->CleanupObjects( 2, &stmt, &temp_buf );

	TRANS_OUTFUNCRET( result )
};



//
// Accepts a Field_Descriptr and Data_Structures buffer as input
//
void FixFieldType( XCBuffer* format, XCBuffer* field, XCBuffer* ds, XCTransactionVars* vars, bool ds_change ) {
	INFUNC( FixFieldType, NULL );

	int type;
	int size;

	if (IsDatabaseBuffer( format )) {
		//
		// Non screen formats
		//
		short int dec = 0;

		// Grab the field_descr data-type
		type = field->GetField( FLD_DATA_TYPE )->GetLong(true);

		// Attempt to fix invalid database types
		const tdata_type_defnition* defn = GetDataTypeDefn( type );
		if (defn->database_type != type) {
			type = defn->database_type;
			defn = GetDataTypeDefn( type );
		};

		// If we didn't get a good type, then send error message
		if (defn->database_type != type) {
			type = field->GetField( FLD_DATA_TYPE )->GetLong(true);
			defn = GetDataTypeDefn( type );

			RAISETOOLSERROR_1PARAM( ERR_INV_FMT_DATA_TYPE, defn->type_name );
		};

		// Does the field have a set size in the database, or zero length
		if (defn->db_size || ds->GetField( FLD_MAX_LENGTH )==0)
			size = defn->db_size;
		else
			size = field->GetField( FLD_DB_BYTE_LENGTH )->GetShort();

		// Set the field size
		ds->GetField( FLD_MIN_LENGTH )->SetShort( 0 );
		ds->GetField( FLD_MAX_LENGTH )->SetShort( size );

		// If a literal val, keep decimal places
		if (type == LITERAL_VAL)
			dec = field->GetField( FLD_DECIMAL_PLACES )->GetShort();
		ds->GetField( FLD_DECIMAL_PLACES )->SetShort( dec );
	} else {
		//
		// Screen formats
		//
		long ftype = format->GetField( FLD_FORMAT_TYPE )->GetLong( true );

		// Grab the data-structures data-type
		type = ds->GetField( FLD_DATA_TYPE )->GetLong(true);

		const tdata_type_defnition* defn = GetDataTypeDefn( type );

		// Fix invalid screen types - on all other types, all data types are valid
		if (ftype == 3 || ftype == 6) {
			if (defn->screen_type != type && type != -1) {
				type = defn->screen_type;
				if (type == -1) {
					// This is a type that won't convert directly to the screen, so
					// take field descriptor values
					type = field->GetField( FLD_DATA_TYPE )->GetLong(true);
				};

				if (type != -1)
					defn = GetDataTypeDefn( type );
			};
			if (defn->screen_type != type && type != -1) {
				// Attempt to take field definition
				type = field->GetField( FLD_DATA_TYPE )->GetLong(true);
				defn = GetDataTypeDefn( type );
			};
			if (defn->screen_type != type) {
				type = ds->GetField( FLD_DATA_TYPE )->GetLong(true);
				defn = GetDataTypeDefn( type );
				RAISETOOLSERROR_1PARAM( ERR_INV_FMT_DATA_TYPE, defn->type_name );
			};
		};

		// Set the literal group/decimal places
		if (!ds_change)
			ds->GetField( FLD_DECIMAL_PLACES )->SetShort( field->GetField( FLD_DECIMAL_PLACES )->GetShort() );

		// Set the field size
		ds->GetField( FLD_MIN_LENGTH )->SetShort( 0 );
		if (defn->screen_size)
			ds->GetField( FLD_MAX_LENGTH )->SetShort( defn->screen_size );
		else {
			if (!ds_change)
				ds->MoveUnlikeField( FLD_MAX_LENGTH, field, FLD_DB_BYTE_LENGTH );
		};
	};

	// Alter type to match our type
	ds->GetField( FLD_DATA_TYPE )->SetShort( type );

	// Write the changes
	DataStructureChange( ds, NULL, vars );

	OUTFUNC();
};


//@ SEC 5
//@ CHA Is Valid Data Type
//@ COD IsValidDataType
//@ GEN Is Valid Data Type returns true as long as the FIELD_VALUE field in the supplied 
//@ GEN buffer can be converted to the data type in the DATA_TYPE field.  Otherwise, it
//@ GEN returns false.
bool IsValidDataType( XCBuffer* buf, XCTransactionVars* vars )
{
	TRANS_INFUNC( IsValidDataType );

	XCBuffer* from = NULL;
	XCBuffer* to = NULL;
	long dtype;

	try {
		dtype = buf->GetField( FLD_DATA_TYPE )->GetLong( true );
		long from_length;
		int from_dec_places = 0;
		int from_type;
		int from_min_len = 0;
		long to_length;
		int to_dec_places = 0;
		int to_type = dtype;
		int to_min_len = 0;
		if ( dtype == DEC_FLOAT || 
			dtype == DEC_DOUBLE || 
			dtype == S_INTEGER || 
			dtype == D_INTEGER ||
			dtype == DECIMAL_64 ||
			dtype == AUTONUMBER )
		{
			from_length = 16;
			to_length = 16;
			from_type = DISPLAY_NUM;
			if ( dtype == DEC_FLOAT || dtype == DEC_DOUBLE || dtype == DECIMAL_64 )
			{
				from_dec_places = 1;
				to_dec_places = 0;
			};
		}
		else
		{
			from_length = strlen( buf->GetField( FLD_FIELD_VALUE )->GetStringPtr() )+1;
			to_length = from_length;

			if (dtype != DISPLAY_NUM)
				from_type = STRING;
			else
				from_type = DISPLAY_NUM;
		};
//		App->ToolsTrace( TRC_APPINTERFACE, TLV_EVERYTHING, "===> From Type : %ld", from_type );
//		App->ToolsTrace( TRC_APPINTERFACE, TLV_EVERYTHING, "===> To Type : %ld", to_type );
		from = App->CreateDynamicField( "FROM", from_length, from_dec_places, from_type, from_min_len );
		from->GetField( -1 )->SetString( buf->GetField( FLD_FIELD_VALUE )->GetStringPtr() );

		to = App->CreateDynamicField( "TO", to_length, to_dec_places, to_type, to_min_len );
		to->MoveField( from, -1 );
//		App->ToolsTrace( TRC_APPINTERFACE, TLV_EVERYTHING, "==> Value : %s", to->GetFieldByIndex( 0 )->GetOffset() );
	} catch( ... ) {
		vars->Errors->Clear();
		App->CleanupObjects( 2, &from, &to );
		TRANS_RETURNFUNC( false );
	};

	App->CleanupObjects( 2, &from, &to );
	TRANS_OUTFUNCRET( true );
};



bool IsDatabaseBuffer( XCBuffer* buf ) {
	return buf->GetField( FLD_FORMAT_TYPE )->Compare( 3 ) < 0;
};



bool SystemObject( XCField* field ) {
	int l = field->GetLong( true );
	return l > 0;
};



void OutputBuffer( XCBuffer* buffer, XCTransport* output ) {
	char temp[ 1024 ];
	sprintf( temp, "\t\t<Buffer Name=\"%s\">\n", buffer->GetFormatName() );
	output->Send( temp );

	for (int i=0; i<buffer->GetFieldCount(); i++) {
		XCField* ftemp = buffer->GetFieldByIndex( i );

		// Output field tag
		sprintf( temp, "\t\t\t<Field Name=\"%s\">", ftemp->GetFieldName() );
		output->Send( temp );

		// Output data
		ftemp->OutputTransport( output, temp );

		output->Send( "</Field>\n" );
	};

	output->Send( "\t\t</Buffer>\n" );
};



// Writes a message to the customizer log file
void LogTransaction( XCTransactionInfo* transaction, XCTransactionVars* vars ) {
	XCFileTransport output( transaction->GetOwner() );
	char filename[ 1024 ];
	char* temp = filename;
	
	temp += sprintf( temp, "%sLogs", vars->myapp->root );

	if (XCFileExists( filename )) {
		temp += sprintf( temp, "%scustomizer.log", OSFILEDELIM );

		transaction_defn* defn = transaction->GetOwner()->GetTransaction( transaction->GetTransNumber() );
		assert( defn );

		output.Open( filename, read_write, open_always );
		output.Lock();

		try {
			if (output.Eof())
				output.Send( "<CustomizerLog>\n" );
			else {
				output.Seek( -16, seek_end );
				output.Send( "\n" );
			};

			sprintf( temp, "\t<Transaction Name='%s'>\n", defn->trans_code );
			output.Send( temp );

			// Write who-dunit
			sprintf( temp, "\t\t<User>%s</User>\n", transaction->GetUser() );
			output.Send( temp );

			// Write timestamp
			time_t ltime;
			tm *newtime;

			time( &ltime );
			newtime = gmtime( &ltime );

			sprintf( temp, "\t\t<Time>%2.2d/%2.2d/%4.4d %2.2d:%2.2d:%2.2d UTC</Time>\n", 
				newtime->tm_mon+1, newtime->tm_mday, newtime->tm_year+1900,
				newtime->tm_hour, newtime->tm_min, newtime->tm_sec );
			output.Send( temp );

			// Write transaction information
			OutputBuffer( vars->buffer, &output );

			XCBuffer** detail = vars->detail;
			for (int i=0; i < vars->detailcount; i++) {
				OutputBuffer( *detail, &output );
				detail++;
			};

			output.Send( "\t</Transaction>\n" );
			output.Send( "</CustomizerLog>" );
		} catch( ... ) {
			output.Unlock();
			throw;
		};
		output.Unlock();
	};
	output.Close();
};




long SelectCountMany( long table, XCTransactionVars* vars, XCField** FieldList ) {
	INFUNC( SelectCountMany, vars->myapp );

	XCSQLGen* query = vars->query;
	static XCCompare comp;
	long result = 0;

	XCBuffer* buf = NULL;
	XCBuffer* bCount = NULL;
	XCDBStatement* stmt = NULL;

	try {
		// Create temp buffer
		buf = vars->myapp->CreateBuffer( table, false );

		// Generate SQL statement
		bCount = query->MakeCOUNT( buf );
		query->AddWHERE( FieldList, &comp );

		stmt = vars->dbtxn->ExecuteSQL( query );
		if (!stmt->Eof())
			result = bCount->GetFieldByIndex(0)->GetLong();
	} catch( ... ) {
		delete [] FieldList;
		vars->myapp->CleanupObjects( 3, &buf, &bCount, &stmt );
		throw;
	};
	delete [] FieldList;
	vars->myapp->CleanupObjects( 3, &buf, &bCount, &stmt );

	OUTFUNCRET( result );
};




long SelectCountMany( long table, XCTransactionVars* vars, long numkeys, ... ) {
	INFUNC( SelectCountMany, vars->myapp );

	XCSQLGen* query = vars->query;
	XCField** FieldList = new XCField*[ numkeys + 1 ];
	XCCompare* comp = new XCCompare[ numkeys ];
	long result = 0;

	XCBuffer* buf = NULL;
	XCBuffer* bCount = NULL;
	XCDBStatement* stmt = NULL;

	try {
		// Create temp buffer
		buf = vars->myapp->CreateBuffer( table, false );

		// Move over key information
		va_list args;
		va_start(args, numkeys);
		for (int i=0; i<numkeys; i++) {
			FieldList[i] = va_arg( args, XCField* );
		};
		FieldList[numkeys] = NULL;

		// Generate SQL statement
		bCount = query->MakeCOUNT( buf );
		query->AddWHERE( FieldList, comp );

		stmt = vars->dbtxn->ExecuteSQL( query );
		if (!stmt->Eof())
			result = bCount->GetFieldByIndex(0)->GetLong();
	} catch( ... ) {
		delete [] FieldList;
		delete [] comp;
		vars->myapp->CleanupObjects( 3, &buf, &bCount, &stmt );
		throw;
	};
	delete [] FieldList;
	delete [] comp;
	vars->myapp->CleanupObjects( 3, &buf, &bCount, &stmt );
	OUTFUNCRET( result );
};



/////////////////////////////
//
// Library initialization
//
/////////////////////////////

extern "C" {

__EXPORT bool XCTransactionCode( XCTransRegServer* svr ) {
	printf( "Loading customizer base\n" );

	// Designer extensions
	svr->RegisterTransaction( TRANS_DR_GETFORMATINFO, DesignerGetFormatInfo );
	svr->RegisterTransaction( TRANS_DR_GETMESSAGES, DesignerGetMessages );
	svr->RegisterTransaction( TRANS_DR_GETLANGUAGES, DesignerGetLanguages );
	svr->RegisterTransaction( TRANS_DR_GETDEFAULTS, DesignerGetDefaults );

	// From formats...
	svr->RegisterTransaction( TRANS_GETFORMATNUMBER, RevFormat );
	svr->RegisterTransaction( TRANS_REVFORMATS, RevFormats );
	svr->RegisterTransaction( TRANS_REVFORMATSCOMP, RevFormatsComponent );
	svr->RegisterTransaction( TRANS_PRINTFORMAT, PrintFormat );

	// From lit relate
	svr->RegisterTransaction( TRANS_REVLITGROUP, RevLitGroup );

	// From data_structures
	svr->RegisterTransaction( TRANS_REVDATASTRUCTURE, RevDataStructure );

	// From transactions
	svr->RegisterTransaction( TRANS_REVTRAN, ReviewTransaction );
	svr->RegisterTransaction( TRANS_REVTRANS, RevTransactions );

	return true;
};

};

